--------------------------------------
--   Matias Blanco & Sergio Perez   --
-- Top Manta - Inventadas ADA Citas --
--------------------------------------

with Ada.Text_Io ; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Float_Random ; use Ada.Numerics.Float_Random;

procedure TopMantaCitasAda is

   G: Generator;

   -- Constantes
   MAX_PIRATAS: constant Integer:= 10; -- tipos que copian CDs
   MAX_CDs_ALMACEN: constant Integer:= 2000; -- CDs que caben en un almacen

   MAX_CDs_PIDEN: constant Integer:= 200; -- Numero de CDs que piden como mucho
   MIN_CDs_PIDEN: constant Integer:= 100; -- Minimo de CDs que pueden pedir

   MAX_ALMACENES: constant Integer:= 4; -- Almacenes por los que se puede mover la mercancia

   -- Tipos y Variables
   type TAlmacenes is array (1..MAX_ALMACENES) of Integer;

   type TPiratasCitados is array (1..MAX_PIRATAS) of Boolean;

   task Policia;

   task Poligono is
      entry Pilla_CDs(Id: in Integer; N_CDs: in Integer;  OK: in out Boolean);
      entry Llenar; -- Para reponer
      entry Mover(N: in Integer);
      entry PoliInvestiga(NumAlmacen: in Integer; N_CDs: in out Integer); -- La poli quiere investigar
      entry PoliRequisa(NumAlmacen: in Integer);
   end Poligono;

   task PePing is
      entry Reponer(Id: in Integer);
      entry Soplo(NumAlmacen: in Integer); -- Id avisa de que viene la poli a ese almacen
      entry Carcel; -- A la carcel, le han pillado, pero se librara chivandose...
   end Peping;

   task type Piratilla is
      entry Init(Idd: in Integer);
   end Piratilla;

   Piratillas: array (1..MAX_PIRATAS) of Piratilla;

   task Citacion is
      entry MandarCitacion(Id: in Integer);
      entry VerTablon(Id: in Integer; Denuncia: in out Boolean);
   end Citacion;

   task body Citacion is
      Denuncias: TPiratasCitados;
   begin
      for I in 1..MAX_PIRATAS loop
         Denuncias(I):= False;
      end loop;
      loop
         select
            accept MandarCitacion(Id: in Integer) do
               Denuncias(Id):= True;
            end MandarCitacion;
         or
            accept VerTablon(Id: in Integer; Denuncia: in out Boolean) do
               Denuncia:=Denuncias(Id);
               Denuncias(Id):=False;
            end VerTablon;
         end select;
      end loop;
   end Citacion;

   task body Poligono is
      Almacenes: TAlmacenes; -- Num de CDs en los almacenes
      AuxContador: Integer;
      Requisado: Integer;

   begin
      Requisado:= 0;
      for I in 1..MAX_ALMACENES loop
         Put("Se ha llenado el almacen " & Integer'Image(I));New_Line;
         Almacenes(I):= MAX_CDs_ALMACEN;
      end loop;

      loop
         select
            when Llenar'Count = 0 =>
               accept Pilla_CDs(Id: in Integer; N_CDs: in Integer; OK: in out Boolean) do
                  Put("El piratilla " & Integer'Image(Id) & " quiere coger " & Integer'Image(N_CDs) & " CDs");New_Line;
                  -- Vemos si se los podemos dar
                  AuxContador:= 0;
                  for I in 1..MAX_ALMACENES loop
                     AuxContador:= AuxContador + Almacenes(I);
                  end loop;
                  if (N_CDs <= AuxContador) then
                     OK:= True;
                     AuxContador:= 0;
                     for I in 1..MAX_ALMACENES loop
                        if (Almacenes(I)-AuxContador >= N_CDs) then
                           -- Con este almacen nos bastara...
                           Almacenes(I):= Almacenes(I) - (N_CDs - AuxContador);
                           AuxContador:= N_CDs;
                        else
                           -- No basta con este almacen... lo vaciamos y pasamos a otro.
                           AuxContador:= AuxContador + Almacenes(I);
                           Almacenes(I) := 0;
                        end if;
                        exit when (AuxContador = N_CDs); -- Salimos del if cuando tengamos suficientes.
                     end loop;
                  else
                     OK:= False;
                  end if;
               end Pilla_CDs;
         or
            accept Llenar do
               for I in 1..MAX_ALMACENES loop
                  Put("Se ha llenado el almacen " & Integer'Image(I));New_Line;
                  Almacenes(I):= MAX_CDs_ALMACEN;
                  delay 1.0;
               end loop;
            end Llenar;
         or
            accept Mover(N: Integer) do
               if (Almacenes(N) > 0) then
                  Put("Moviendo CDs al siguiente almacen desde ");Put(N,0);New_Line;
                  if (N=MAX_ALMACENES) then
                     Almacenes(1):=Almacenes(1) + Almacenes(N);
                  else
                     Almacenes(N+1):=Almacenes(N+1)+Almacenes(N);
                  end if;
                  Almacenes(N):=0;
               else
                  Put("No hay CDs en ese almacen");New_Line;
               end if;
            end Mover;
         or
            accept PoliInvestiga(NumAlmacen: in Integer; N_CDs: in out Integer) do
               N_CDs:= Almacenes(NumAlmacen);
            end PoliInvestiga;
         or
            accept PoliRequisa(NumAlmacen: in Integer) do
               Almacenes(NumAlmacen):=0;
            end PoliRequisa;
         end select;
      end loop;
   end Poligono;

   task body PePing is
      Reponiendo: Boolean;

   begin
      Reponiendo:=False;

      loop
         select
            accept Reponer(Id: in Integer) do
               if (not Reponiendo) then
                  Put("PePing recibe la llamada del piratilla " & Integer'Image(Id) & " avisando de que no hay CDs sufientes para el");New_Line;
                  Reponiendo := True;
                  Poligono.Llenar;
                  Reponiendo := False;
               end if;
            end Reponer;


         or
            accept Soplo(NumAlmacen: in Integer) do
               Put("La poli filtra a PePing que se va a investigar el almacen " & Integer'Image(NumAlmacen));New_Line;
               Poligono.Mover(NumAlmacen);
            end Soplo;
            or
               accept Carcel do
                  Put("PePing a la carcel, pero... para librarse...");New_Line;
                  for I in 1..MAX_PIRATAS loop
                     if (Random(G) < 0.3) then
                        Put("... inculpa y manda al piratilla " & Integer'Image(I) & " a la carcel x(");New_Line;
                        Citacion.MandarCitacion(I);
                     end if;
                  end loop;
               end Carcel;
         end select;
         end loop;
   end Peping;

   task body Piratilla is
      Id: Integer;
      N_CDs: Integer;
      StockCDs: Integer;
      OK: Boolean;
      CarcelFlag: Boolean;

   begin
      accept Init(Idd: in Integer) do
         Id:= Idd;
         N_CDs:= Integer(Float(MAX_CDS_PIDEN-MIN_CDS_PIDEN)*Random(G))+MIN_CDS_PIDEN;
         StockCDs:=N_CDs;
         CarcelFlag:=False;
      end Init;
      loop
            -- Graba
              Put("El piratilla " & Integer'Image(Id) & " están grabando");New_Line;
              delay Duration(0.1*Float(N_CDs)*Random(G));
              -- Mira si le han pillado
              Citacion.VerTablon(Id, CarcelFlag);
              if (CarcelFlag) then
                 Put("El pirata " & Integer'Image(Id) & " se va a la carcel");New_Line;
                 delay 20.0;
                 Put("El pirata " & Integer'Image(Id) & " sale de la carcel");New_Line;
              end if;
              --
              Put("El piratilla " & Integer'Image(Id) & " necesita CDs...");New_Line;
              OK:=False;
              while (not OK) loop
                 Citacion.VerTablon(Id, CarcelFlag);
                 if (CarcelFlag) then
                    Put("El piratilla " & Integer'Image(Id) & " se libra de la carcel porque no tiene CDs!");New_Line;
                 end if;
                 Poligono.Pilla_CDs(Id, N_CDs, OK);
                 if (not OK) then
                    Put("El piratilla " & Integer'Image(Id) & " avisa a PePing");New_Line;
                    PePing.Reponer(Id);
                    Put("*");
                 end if;
              end loop;
              Put("El piratilla " & Integer'Image(Id) & " consigue los CDs (");Put(N_CDs,0);Put(")");New_Line;

      end loop;
   end Piratilla;

   task body Policia is
      NumeroAleatorio: Float;
      AlmacenInvestigado: Integer;
      CDsEncautados: Integer;
   begin
      Reset(G);
      AlmacenInvestigado:= 1; -- Empiezan por el primero.
      -- Se dan vueltas por todos los almacenes y deciden o no si van a investigar
      loop
         NumeroAleatorio:= Random(G);
         delay Duration(NumeroAleatorio * 3.0 + 3.0); -- Un cafetillo hasta ir a otro almacen.

         NumeroAleatorio:= Random(G);
         CDsEncautados := 0;
         if (NumeroAleatorio < 0.8) then
            if (Random(G) < 0.5) then
               PePing.Soplo(AlmacenInvestigado);
               delay 1.0;
            end if;
            Poligono.PoliInvestiga(AlmacenInvestigado, CDsEncautados);
            if (CDsEncautados > 0) then
               PePing.Carcel; -- Ramoncin estara contento.
               Put("Se requisan " & Integer'Image(CdsEncautados) & " CDs del almacen " & Integer'Image(AlmacenInvestigado));New_Line;
               Poligono.PoliRequisa(AlmacenInvestigado);
            end if;
         end if;


         AlmacenInvestigado:= AlmacenInvestigado + 1;

         if (AlmacenInvestigado = MAX_ALMACENES + 1) then
            AlmacenInvestigado:= 1;
         end if;

      end loop;
   end Policia;


begin
--   Put("Matias Blanco & Sergio Perez - TopManta");New_Line; -- Egocentrismo

   Reset(G);
   for I in 1..MAX_PIRATAS loop
      Piratillas(I).Init(I);
   end loop;
end TopMantaCitasAda;
